﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections;
using System.Runtime.Serialization;
using System.Text;
using System.Security.Permissions;
using System.Runtime.InteropServices;

namespace Heaven.Mathematics
{
    /// <summary>
    /// Трехмерная матрица
    /// </summary>
    public struct Matrix3 : ICloneable
	{
		#region Атрибуты

		private double m11, m12, m13;
		private double m21, m22, m23;
		private double m31, m32, m33;

		#endregion

		#region Конструкторы

		/// <summary>
		/// Инициализация матрицы поэлементно
		/// </summary>
		public Matrix3(
			double m11, double m12, double m13,
			double m21, double m22, double m23,
			double m31, double m32, double m33
			)
		{
            this.m11 = m11; this.m12 = m12; this.m13 = m13;
            this.m21 = m21; this.m22 = m22; this.m23 = m23;
            this.m31 = m31; this.m32 = m32; this.m33 = m33;
		}

		/// <summary>
		/// Инициализация матрицы поэлементно
		/// </summary>
		/// <param name="elements">Массив вещественных одинарной точности (построчный перебор)</param>
		public Matrix3(float[] elements)
		{
			Debug.Assert(elements != null);
			Debug.Assert(elements.Length >= 9);

			m11 = elements[0]; m12 = elements[1]; m13 = elements[2];
			m21 = elements[3]; m22 = elements[4]; m23 = elements[5];
			m31 = elements[6]; m32 = elements[7]; m33 = elements[7];
		}

        /// <summary>
		/// Инициализация матрицы заданием столбцов
		/// </summary>
		/// <param name="column1">Вектор, первый столбик</param>
        /// <param name="column2">Вектор, второй столбик</param>
        /// <param name="column3">Вектор, третий столбик</param>
		public Matrix3(Vector3 column1, Vector3 column2, Vector3 column3)
		{
			m11 = column1.X; m12 = column2.X; m13 = column3.X;
			m21 = column1.Y; m22 = column2.Y; m23 = column3.Y;
			m31 = column1.Z; m32 = column2.Z; m33 = column3.Z;
		}

		/// <summary>
		/// Конструктор копирования
		/// </summary>
		public Matrix3(Matrix3 m)
		{
			m11 = m.M11; m12 = m.M12; m13 = m.M13;
			m21 = m.M21; m22 = m.M22; m23 = m.M23;
			m31 = m.M31; m32 = m.M32; m33 = m.M33;
		}


		#endregion

		#region Константы

        /// <summary>
        /// Трехмерная нулевая матрица из вещественных одинарной точности
        /// </summary>
        public static readonly Matrix3 Zero = new Matrix3(0,0,0,0,0,0,0,0,0);

        /// <summary>
        /// Трехмерная единичная матрица из вещественных одинарной точности
        /// </summary>
        public static readonly Matrix3 Identity = new Matrix3(
			1,0,0,
			0,1,0,
			0,0,1
			);

		#endregion

        #region Методы клонирования

        /// <summary>
        /// Создание точной копии объекта
        /// </summary>
        /// <returns>Матрица, приведенная к типу object</returns>
        object ICloneable.Clone()
		{
			return new Matrix3(this);
		}
        /// <summary>
        /// Создает точную копию матрицы
        /// </summary>
        /// <returns>Клон исходной матрицы</returns>
        public Matrix3 Clone()
		{
			return new Matrix3(this);
		}

		#endregion

        #region Статические арифметические методы

        /// <summary>
        /// Сложение двух матриц
        /// </summary>
        /// <param name="a">Слагаемое матрица 3x3</param>
        /// <param name="b">Слагаемое матрица 3x3</param>
        /// <returns>Новый экземпляр матрицы - сумма</returns>
        public static Matrix3 Add(Matrix3 a, Matrix3 b)
		{
			return new Matrix3(
				a.M11 + b.M11, a.M12 + b.M12, a.M13 + b.M13,
				a.M21 + b.M21, a.M22 + b.M22, a.M23 + b.M23,
				a.M31 + b.M31, a.M32 + b.M32, a.M33 + b.M33
				);
		}

        /// <summary>
        /// Сложение матрицы и скаляра
        /// </summary>
        /// <param name="a">Матрица 3х3</param>
        /// <param name="s">Скаляр</param>
        /// <returns>Новый экземпляр матрицы - сумма</returns>
        public static Matrix3 Add(Matrix3 a, double s)
		{
			return new Matrix3(
				a.M11 + s, a.M12 + s, a.M13 + s, 
				a.M21 + s, a.M22 + s, a.M23 + s,
				a.M31 + s, a.M32 + s, a.M33 + s
				);
		}

 
        /// <summary>
        /// Разница двух матриц
        /// </summary>
        /// <param name="a">Уменьшаемая матрица 3х3</param>
        /// <param name="b">Вычитаемая матрица 3х3</param>
        /// <returns></returns>
        /// <remarks>result[x][y] = a[x][y] - b[x][y]</remarks>
        public static Matrix3 Subtract(Matrix3 a, Matrix3 b)
		{
			return new Matrix3(
				a.M11 - b.M11, a.M12 - b.M12, a.M13 - b.M13,
				a.M21 - b.M21, a.M22 - b.M22, a.M23 - b.M23,
				a.M31 - b.M31, a.M32 - b.M32, a.M33 - b.M33
				);
		}

        /// <summary>
        /// Разница матрицы и скаляра
        /// </summary>
        /// <param name="a">Умешьшаемое - матрица 3х3</param>
        /// <param name="s">Вычитаемое - скаляр</param>
        /// <returns>Новый экземпляр матрицы 3х3, содержащий разницу</returns>
        public static Matrix3 Subtract(Matrix3 a, double s)
		{
			return new Matrix3(
				a.M11 - s, a.M12 - s, a.M13 - s,
				a.M21 - s, a.M22 - s, a.M23 - s,
				a.M31 - s, a.M32 - s, a.M33 - s
				);
		}


        /// <summary>
        /// Произведение двух матриц
        /// </summary>
        /// <param name="a">Матрица 3х3</param>
        /// <param name="b">Матрица 3х3</param>
        /// <returns>Новый объект матрицы 3х3, содержащий произведение</returns>
        public static Matrix3 Multiply(Matrix3 a, Matrix3 b)
		{
			return new Matrix3(
				a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31,
				a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32,
				a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33,

				a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31,
				a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32,
				a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33,

				a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31,
				a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32, 
				a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33
				);
		}


        /// <summary>
        /// Трансформация передаваемого вектора матрицей
        /// </summary>
        /// <param name="matrix">Матрица 3х3, задающая трансформацию</param>
        /// <param name="vector">Исходный вектор</param>
        /// <returns>Новый объект вектора, содержащий трансформированный исходный</returns>
        public static Vector3 Transform(Matrix3 matrix, Vector3 vector)
		{
			return new Vector3(
				(matrix.M11 * vector.X) + (matrix.M12 * vector.Y) + (matrix.M13 * vector.Z),
				(matrix.M21 * vector.X) + (matrix.M22 * vector.Y) + (matrix.M23 * vector.Z),
				(matrix.M31 * vector.X) + (matrix.M32 * vector.Y) + (matrix.M33 * vector.Z));
		}


        /// <summary>
        /// Транспонирование матрицы
        /// </summary>
        /// <param name="m">Исходная матрица 3х3</param>
        /// <returns>Новый объект матрицы 3х3, содержащая результат - транспонированную матрицу</returns>
        public static Matrix3 Transpose(Matrix3 m)
		{
			Matrix3 t = new Matrix3(m);
			t.Transpose();
			return t;
		}

		#endregion

		#region Свойства

		/// <summary>
        /// Получение и установка элемента [1,1]
        /// </summary>
		public double M11
		{
			get { return m11; }
			set { m11 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [1,2]
        /// </summary>
		public double M12
		{
			get { return m12; }
			set { m12 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [1,3]
        /// </summary>
		public double M13
		{
			get { return m13; }
			set { m13 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [2,1]
        /// </summary>
		public double M21
		{
			get { return m21; }
			set { m21 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [2,2]
        /// </summary>
		public double M22
		{
			get { return m22; }
			set { m22 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [2,3]
        /// </summary>
		public double M23
		{
			get { return m23; }
			set { m23 = value;}
		}
        
		/// <summary>
        /// Получение и установка элемента [3,1]
        /// </summary>
		public double M31
		{
			get { return m31; }
			set { m31 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [3,2]
        /// </summary>
		public double M32
		{
			get { return m32; }
			set { m32 = value;}
		}

		/// <summary>
        /// Получение и установка элемента [3,3]
        /// </summary>
		public double M33
		{
			get { return m33; }
			set { m33 = value;}
		}

		#endregion

        #region Перегруженные методы

        /// <summary>
        /// Получение хасп-ключа для этого экземпляра
        /// </summary>
        /// <returns>32-битное знаковое целое</returns>
        public override int GetHashCode()
		{
			return 
				m11.GetHashCode() ^ m12.GetHashCode() ^ m13.GetHashCode() ^
				m21.GetHashCode() ^ m22.GetHashCode() ^ m23.GetHashCode() ^
				m31.GetHashCode() ^ m32.GetHashCode() ^ m33.GetHashCode();
		}

		/// <summary>
        /// Проверка эквивалентности этого объекта с другим
		/// </summary>
		/// <param name="obj">Объект для сравнения</param>
		/// <returns>Истина если объекты эквивалентны</returns>
		public override bool Equals(object obj)
		{
			if (obj is Matrix3)
			{
				Matrix3 m = (Matrix3)obj;
				return 
					(m11 == m.M11) && (m12 == m.M12) && (m13 == m.M13) &&
					(m21 == m.M21) && (m22 == m.M22) && (m23 == m.M23) &&
					(m31 == m.M31) && (m32 == m.M32) && (m33 == m.M33);
			}
			return false;
		}

        /// <summary>
        /// Конвертация матрицы в строку
        /// </summary>
        /// <returns>Строка, представляющая матрицу</returns>
        public override string ToString()
		{
			StringBuilder s = new StringBuilder();
			s.Append(String.Format( "|{0}, {1}, {3}|\n", m11, m12, m13));
			s.Append(String.Format( "|{0}, {1}, {3}|\n", m21, m22, m23));
			s.Append(String.Format( "|{0}, {1}, {3}|\n", m31, m32, m33));

			return s.ToString();
		}

		#endregion

		#region Методы

		/// <summary>
		/// Подсчет детерминанта матрицы
		/// </summary>
		/// <returns>Скаляр, значение</returns>
		public double Determinant()
		{
			return 
				m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32 -
				m13 * m22 * m31 - m11 * m23 * m32 - m12 * m21 * m33;
		}

        /// <summary>
        /// Подсчет средней линии - сумма диагональных элементов
        /// </summary>
        /// <returns>Возвращает сумму диагональных элементов</returns>
        public double Trace()
		{
			return m11 + m22 + m33;
		}

        /// <summary>
        /// Транспонирование матрицы
        /// </summary>
        public void Transpose()
		{
			Swap(ref m12, ref m21);
			Swap(ref m13, ref m31);
			Swap(ref m23, ref m32);
		}

        void Swap(ref double a, ref double b)
        {
            double c = a;
            a = b;
            b = c;
        }

		#endregion

		#region Операторы сравнения

		/// <summary>
		/// Проверка на эквивалентность двух матриц
		/// </summary>
		/// <param name="a">Матрица 3х3, левосторонний операнд</param>
        /// <param name="b">Матрица 3х3, правосторонний операнд</param>
        /// <returns>Истина, если матрицы равны, иначе ложь</returns>
        public static bool operator==(Matrix3 a, Matrix3 b)
		{
			return ValueType.Equals(a,b);
		}

		/// <summary>
		/// Проверка на отличность двух матриц 3х3
		/// </summary>
        /// <param name="a">Матрица 3х3, левосторонний операнд</param>
        /// <param name="b">Матрица 3х3, правосторонний операнд</param>
        /// <returns>Истина, если матрицы различны, иначе ложь</returns>
        public static bool operator!=(Matrix3 a, Matrix3 b)
		{
			return !ValueType.Equals(a,b);	
		}

		#endregion

		#region Бинарные операторы

        /// <summary>
        /// Сложение двух матриц
        /// </summary>
        /// <param name="a">Матрица 3х3, левосторонний операнд</param>
        /// <param name="b">Матрица 3х3, правосторонний операнд</param>
        /// <returns>Новый объект матрицы 3х3, содержащий сумму</returns>
        public static Matrix3 operator+(Matrix3 a, Matrix3 b)
		{
			return Matrix3.Add(a,b);
		}

        /// <summary>
        /// Сложение матрицы и скаляра
        /// </summary>
        /// <param name="a">Матрица 3х3, левосторонний операнд</param>
        /// <param name="s">Скаляр, правосторонний операнд</param>
        /// <returns>Новый объект матрицы 3х3, содержащий сумму</returns>
        public static Matrix3 operator+(Matrix3 a, double s)
		{
			return Matrix3.Add(a,s);
		}

        /// <summary>
        /// Сложение скаляра и матрицы
        /// </summary>
        /// <param name="s">Скаляр, левосторонний операнд</param>
        /// <param name="a">Матрица 3х3, правосторонний операнд</param>
        /// <returns>Новый объект матрицы 3х3, содержащий сумму</returns>
        public static Matrix3 operator+(double s, Matrix3 a)
		{
			return Matrix3.Add(a,s);
		}

        /// <summary>
        /// Разница двух матриц
        /// </summary>
        /// <param name="a">Матрица 3х3, левосторонний операнд</param>
        /// <param name="b">Матрица 3х3, правосторонний операнд</param>
        /// <returns>Новый объект матрицы 3х3, содержащий разницу</returns>
        public static Matrix3 operator-(Matrix3 a, Matrix3 b)
		{
			return Matrix3.Subtract(a,b);
		}

        /// <summary>
        /// Разница матрицы и скаляра
        /// </summary>
        /// <param name="a">Матрица 3х3, уменьшаемое</param>
        /// <param name="s">Скаляр, вычитаемое</param>
        /// <returns>Новый объект матрицы 3х3, содержащий разницу</returns>
        public static Matrix3 operator-(Matrix3 a, double s)
		{
			return Matrix3.Subtract(a,s);
		}

        /// <summary>
        /// Произведение двух матриц
        /// </summary>
        /// <param name="a">Матрица 3х3</param>
        /// <param name="b">Матрица 3х3</param>
        /// <returns>Новый объект матрицы 4х4, содержащий произведение</returns>
        public static Matrix3 operator*(Matrix3 a, Matrix3 b)
		{
			return Matrix3.Multiply(a,b);
		}

        /// <summary>
        /// Умножение матрицы на вектор
        /// </summary>
        /// <param name="matrix">Матрица 3х3</param>
        /// <param name="vector">Четырехмерный вектор</param>
        /// <returns>Трансформированный вектор</returns>
        public static Vector3 operator*(Matrix3 matrix, Vector3 vector)
		{
			return Matrix3.Transform(matrix, vector);
		}

		#endregion

        #region Индексаторы

        /// <summary>
        /// Доступ по индексу
        /// </summary>
        public double this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return m11;
                    case 1: return m12;
                    case 2: return m13;
                    case 3: return m21;
                    case 4: return m22;
                    case 5: return m23;
                    case 6: return m31;
                    case 7: return m32;
                    case 8: return m33;

                    default:
                        throw new InvalidOperationException(
                            "Попытка считать из несуществующего элемента " +
                            index.ToString() + " матрицы 3х3");
                }
            }
            set
            {
                switch (index)
                {
                    case 0: m11 = value; break;
                    case 1: m12 = value; break;
                    case 2: m13 = value; break;
                    case 3: m21 = value; break;
                    case 4: m22 = value; break;
                    case 5: m23 = value; break;
                    case 6: m31 = value; break;
                    case 7: m32 = value; break;
                    case 8: m33 = value; break;

                    default:
                        throw new InvalidOperationException(
                            "Попытка записать в несуществующий элемент " +
                            index.ToString() + " матрицы 3х3");
                }
            }
        }

        /// <summary>
        /// Доступ по индексу строка - столбец
        /// </summary>
        public double this[int row, int column]
		{
			get 
			{
				return this[ row*3 + column ];
			}
			set 
			{				
				this[ row*3 + column ] = value;
			}			
		}

		#endregion
	}
}
